package com.authine.cloudpivot.app.openapi.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.netty.buffer.ByteBufAllocator;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.InputStream;
import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 全局网关
 */
@Slf4j
@Component
public class GatewayGlobalFilter implements GlobalFilter, Ordered {

	private String baseUrl = "http://47.106.39.194";

	private ObjectMapper objectMapper = new ObjectMapper();

	@SneakyThrows
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String url = exchange.getRequest().getURI().getPath();
		log.debug("url = {}", url);
		if (!url.startsWith("/v1")) {
			return chain.filter(exchange);
		}
		//这里只是一个简单的URL重写，具体逻辑还需自己重新实现【推荐做成配置】
		// 如：/v1/goods/purchase/** 企业采购目录  /v1/shop/** 供应商商品  /v1/sourcing/** 商城商品 等等
		if (url.startsWith("/v1/goods/purchase/")) {
			//URL最后就是对应自定义服务的方法名
			String method = url.substring(url.lastIndexOf("/") + 1).replace("-", "");
			url = baseUrl + "/api/launcher/bpccnt2021datacenter/application/invoke?action=/GomroGoodsPurchaseService/" + method;
		}
		if (url.startsWith("/v1/shop/")) {
			//URL最后就是对应自定义服务的方法名
			String method = url.substring(url.lastIndexOf("/") + 1).replace("-", "");
			url = baseUrl + "/api/launcher/bpccnt2021datacenter/application/invoke?action=/GomroShopService/" + method;
		}
		String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
		if (StringUtils.isBlank(contentType)) {
			contentType = MediaType.MULTIPART_FORM_DATA_VALUE;
		}
		/*if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
			//修改请求路径
			exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, new URI(url));
			return chain.filter(exchange);
		}*/
		ServerHttpRequest request = exchange.getRequest().mutate().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).method(HttpMethod.POST).build();
		ServerHttpResponse response = exchange.getResponse();
		ServerHttpRequestDecorator decorator = processRequest(request, response.bufferFactory(), exchange, contentType);
		ServerWebExchange serverWebExchange = exchange.mutate().request(decorator).response(response).build();
		//修改请求路径
		serverWebExchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, new URI(url));

		return chain.filter(serverWebExchange);
	}

	private Map<String, Object> getHeader(HttpHeaders headers) {
		Map<String, Object> header = new HashMap<>();
		headers.keySet().forEach(key -> {
			header.put(key, headers.getFirst(key));
		});
		return header;
	}

	private Map<String, Object> getQuery(MultiValueMap<String, String> queryParams) {
		Map<String, Object> query = new HashMap<>();
		if (CollectionUtils.isEmpty(queryParams)) {
			return query;
		}
		queryParams.keySet().forEach(key -> {
			query.put(key, queryParams.getFirst(key));
		});
		return query;
	}

	@Override
	public int getOrder() {
		return Integer.MAX_VALUE;
	}

	/**
	 * 修改请求参数
	 * @param request
	 * @param bufferFactory
	 * @return
	 */
	private ServerHttpRequestDecorator processRequest(ServerHttpRequest request, DataBufferFactory bufferFactory, ServerWebExchange serverWebExchange, String contentType) {
		JSONObject body = new JSONObject();
		if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
			DataBuffer dataBuffer = request.getBody().blockFirst();
			int len = dataBuffer.readableByteCount();
			byte[] bytes = new byte[len];
			dataBuffer.read(bytes);
			DataBufferUtils.release(dataBuffer);
			String textBody = new String(bytes, StandardCharsets.UTF_8);
			body = JSON.parseObject(textBody);
		} else {
			Map<String, Object> formData = extractValues(serverWebExchange);
			body.putAll(formData);
		}

		Map<String, Object> query = getQuery(request.getQueryParams());
		Map<String, Object> header = getHeader(request.getHeaders());
		JSONObject jsonBody = new JSONObject();
		jsonBody.put("body", body);
		jsonBody.put("query", query);
		//有需要header的话，需要实现getHeader【根据需要筛选header里的参数】
		//jsonBody.put("header", header);
		log.info("jsonBody : {}", jsonBody);
		DataBuffer data = bufferFactory.allocateBuffer();
		data.write(jsonBody.toString().getBytes(StandardCharsets.UTF_8));
		HttpHeaders headers = new HttpHeaders();
		headers.putAll(request.getHeaders());
		headers.remove(HttpHeaders.CONTENT_LENGTH);
		return new ServerHttpRequestDecorator(request) {
			@Override
			public HttpHeaders getHeaders() {
				int contentLength = data.readableByteCount();
				if (contentLength > 0) {
					headers.setContentLength(contentLength);
				} else {
					headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
				}
				return headers;
			};
			@Override
			public Flux<DataBuffer> getBody() {
				return Flux.just(data);
			}
		};
	}

	private Map<String, Object> extractValues(ServerWebExchange exchange) {
		Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
		Mono<MultiValueMap<String, Part>> multipartData = exchange.getMultipartData();
		Mono<Map<String, Object>> monoReuslt = Mono.zip(formData, multipartData).map(tuple -> {
			Map<String, Object> map = new TreeMap<>();
			tuple.getT1().forEach((key, values) -> {
				map.put(key, values.get(0));
			});
			tuple.getT2().forEach((key, values) -> {
				String content = String.valueOf(values.get(0));
				if (content.indexOf("=") != -1) {
					String value = content.split("=")[1];
					map.put(key, value.substring(0, value.length() - 1));
				}

			});
			return map;
		});
		return monoReuslt.block();
	}
}
